/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.lct.eq2.multicast;

import com.lct.eq2.data.Group;
import com.lct.eq2.data.Line;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

/**
 *
 * @author Me
 */
public class Server extends Thread {

    private HashMap playerGroups;
    private HashMap playerGroupIndex;
    private HashMap playerIndex;
    private ServerGUI parentGui;
    private ArrayList dbHelperThreads;
    private int dbHelperPosition;
    private long totalReceived;
    private static final int WORKERS = 2;

    public Server(ServerGUI gui) {
        this.parentGui = gui;
        playerGroups = new HashMap();
        playerIndex = new HashMap();
        playerGroupIndex = new HashMap();
//        this.dbHelperThreads = new ArrayList();
//        for (int x = 0; x < WORKERS; x++) {
//            this.dbHelperThreads.add(new DatabaseHelperThread());
//        }
//        this.dbHelperPosition = 0;
    }

    public void increaseRank(Line line) {
        if (this.dbHelperPosition == WORKERS) {
            this.dbHelperPosition = 0;
        }
        DatabaseHelperThread thread = (DatabaseHelperThread) this.dbHelperThreads.get(this.dbHelperPosition);
        this.dbHelperPosition++;
        thread.increaseRank(line);
        thread = null;
    }

    public void storeToDatabase(ArrayList list) {
        if (this.dbHelperPosition == WORKERS) {
            this.dbHelperPosition = 0;
        }
        DatabaseHelperThread thread = (DatabaseHelperThread) this.dbHelperThreads.get(this.dbHelperPosition);
        this.dbHelperPosition++;
        thread.addAttackListToQueue(list);
        thread = null;
    }

    public void addToTotalReceived(long bytes) {
        this.totalReceived += bytes;
        this.parentGui.setTotalBytesReceived(this.getTotalBytesReceived());
    }

    public long getTotalBytesReceived() {
        return this.totalReceived;
    }

    @Override
    public void run() {
//        for (Object object : dbHelperThreads) {
//            DatabaseHelperThread thread = (DatabaseHelperThread)object;
//            thread.start();
//        }
        System.out.println("Server started. Waiting for connections...");
        ServerSocket ssChannel = null;
        try {
            ssChannel = new ServerSocket(13267);
        } catch (IOException e) {
            //nothing
        }

        while (true) {
            try {
                Socket sChannel = ssChannel.accept();
                if (sChannel != null) {
//                    System.out.println("TestServer:: Client connected.");
                    ServerThread thread = new ServerThread(sChannel, this);
                    thread.start();
                }
            } catch (IOException e) {
                //nothing
            }
        }
    }

    public long getBytesSentByThread(String id) {
        long rval = 0;
        try {
            ServerThread t = (ServerThread) this.playerIndex.get(id.toLowerCase());
            rval = t.getTotalBytesSent();
        } catch (NullPointerException ex) {
            this.clearHungThread(id.toLowerCase());
        }
        return rval;
    }

    public long getBytesReceivedByThread(String id) {
        long rval = 0;
        try {
            ServerThread t = (ServerThread) this.playerIndex.get(id.toLowerCase());
            rval = t.getTotalBytesReceived();
        } catch (NullPointerException ex) {
            this.clearHungThread(id.toLowerCase());
        }
        return rval;
    }

    public void sendData(Object o, String id) {
        //System.out.println(" | " + id + " :: " + o);
        if (o.toString().equalsIgnoreCase("ping")) {
            ServerThread t = (ServerThread) this.playerIndex.get(id.toLowerCase());
            if (t != null) {
                t.sendData(o);
            } else {
                this.clearHungThread(id.toLowerCase());
            }
        } else {
            Iterator iter = this.playerIndex.keySet().iterator();
            ArrayList checked = new ArrayList();
            while (iter.hasNext()) {
                String player = ((String) iter.next()).toLowerCase();
                if (!player.equalsIgnoreCase(id) && !checked.contains(player)) {
                    String groupID = (String) this.playerGroupIndex.get(id.toLowerCase());
                    if (groupID != null) {
                        //System.out.println("Found group for " + player + ", groupID = " + groupID.toLowerCase());
                        ArrayList<String> players = (ArrayList<String>) this.playerGroups.get(groupID.toLowerCase());
                        for (String _player : players) {
                            _player = _player.toLowerCase();
//                            String _player = ((String) object).toLowerCase();
                            //System.out.println("_player: " + _player + ", id: " + id);
                            if (!_player.equalsIgnoreCase(id) && !checked.contains(_player)) {
                                //System.out.println("Getting ServerThread object for " + _player);
                                ServerThread t = (ServerThread)this.playerIndex.get(_player);
                                t.sendData(o);
                                checked.add(_player);
                            }
                        }
                    }
                }
            }
        }
    }

    public void clearHungThread(String id) {
        if (this.playerIndex.containsKey(id.toLowerCase())) {
            this.parentGui.removeClient(id);
            this.removeFromGroup(id.toLowerCase());
        }
    }

    public ArrayList getGroupMembers(String groupID) {
        return (ArrayList)this.playerGroups.get(groupID);
    }

    public Group addToGroup(String addID, String groupID, boolean create) {
        Group rval = null;
        groupID = groupID.toLowerCase();
        addID = addID.toLowerCase();
        if (this.playerGroups.containsKey(groupID)) {
            if (!create) {
                System.out.println(groupID + "'s group exists.");
                if (!groupID.equalsIgnoreCase(addID)) {
                    if (!this.playerGroupIndex.containsKey(addID)) {
                        ArrayList list = this.getGroupMembers(groupID);
                        list.add(addID);
                        this.playerGroups.put(groupID, list);
                        this.playerGroupIndex.put(addID, groupID);
                        rval = new Group(groupID, Group.GROUP_JOINED);
                        rval.setGroupMembers(list);
                    }
                }
            } else {
                rval = rval = new Group(groupID, Group.GROUP_EXISTS);
            }
        } else if (create) {
            System.out.println(groupID + "'s group created");
            ArrayList list = new ArrayList();
            list.add(groupID);
            this.playerGroupIndex.put(groupID, addID);
            this.playerGroups.put(groupID, list);
            rval = new Group(groupID, Group.GROUP_CREATED);
        } else {
            rval = new Group(groupID, Group.NO_GROUP_EXISTS);
        }
        return rval;
    }

    public void addThread(ServerThread t, String id) {
        this.parentGui.addClient(id);
        this.playerIndex.put(id.toLowerCase(), t);
    }

    public void removeThread(ServerThread t, String id) {
        //this.totalReceived += t.getTotalBytesReceived();

        try {
            if (this.playerIndex.containsKey(id.toLowerCase())) {
                this.parentGui.removeClient(id);
                Group group = this.removeFromGroup(id.toLowerCase());
                if (group.getGroupMembers() != null && group.getGroupMembers().size() > 0) {
                    this.notifyGroupMembers(group);
                }
                this.playerIndex.remove(id.toLowerCase());
            }
        } catch (NullPointerException ex) {
            //nothing ?
        }
    }

    public Group removeFromGroup(String id) {
        Group rval = null;

        if (this.playerGroupIndex.containsKey(id.toLowerCase())) {
            String groupID = (String) this.playerGroupIndex.get(id.toLowerCase());
            ArrayList list = (ArrayList) this.playerGroups.get(groupID);
            if (list.size() == 1) {
                this.playerGroups.remove(groupID);
            } else {
                list.remove(id.toLowerCase());
                this.playerGroups.put(groupID, list);
                rval = new Group(groupID, Group.GROUP_PLAYER_LEFT);
                rval.setGroupMembers(list);
            }
            this.playerGroupIndex.remove(id.toLowerCase());
        }
        return rval;
    }

    public void notifyGroupMembers(Group group) {
        for (Object object : group.getGroupMembers()) {
            ServerThread t = (ServerThread) this.playerIndex.get(object);
            t.sendData(group);
        }
    }
}
